/*
 * BufferFrame.cpp
 *
 *  Created on: May 2, 2012
 *      Author: Ka Wai Lo
 */

//#define DEBUG

#include "BufferFrame.hpp"

#include <stdlib.h>
#include <sys/mman.h> // for mmap()
#include <string.h> // for memcpy()
#include <iostream>

using namespace std;

// >>>> Default Constructor <<<<
BufferFrame::BufferFrame(const int vecId) {
	#ifdef DEBUG
	cout << "BufferFrame[" << vecId << "] Constructor invoked.\n";
	if ((PAGE_SIZE & 0xfff) != 0) {
		cerr << "PAGE_SIZE must be multiple of 4096. Now: " << PAGE_SIZE << "\n";
	}
	#endif

	bCopy = false;
	pPageBuffer = (void*) malloc(PAGE_SIZE);
	pFileBuffer = nullptr;
	fd = 0;
	iPageId = 0;
	iVecId = vecId;
	bAlloc = false;
	bDirty = false;

	#ifdef DEBUG
	cout << "BufferFrame" << vecId << " created.\n";
	#endif
}

// >>>> Destructor <<<<
BufferFrame::~BufferFrame() {
	#ifdef DEBUG
	cout << "BufferFrame[" << iVecId << "] Destructor invoked.\n";
	#endif

	if (!bCopy) { free(pPageBuffer); }

	#ifdef DEBUG
	cout << "BufferFrame[" << iVecId << "] destroyed.\n";
	#endif
}

// >>>> Copy Constructor <<<<
BufferFrame::BufferFrame(BufferFrame& o) {
	#ifdef DEBUG
	cout << "BufferFrame Copy Constructor invoked.\n";
	#endif

	*this = o; // Delegate to operator=

	#ifdef DEBUG
	cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "] Copied.\n";
	#endif
}

// >>>> Assignment operator <<<<
BufferFrame& BufferFrame::operator=(BufferFrame& o) {
	#ifdef DEBUG
	cout << "BufferFrame::operator= invoked.\n";
	#endif

	bCopy = true;
	oParent = &o;
	// ==== Copy const variables ====
	pPageBuffer = o.pPageBuffer;
	pFileBuffer = o.pFileBuffer;
	fd = o.fd;
	iPageSize = o.iPageSize;
	iPageId = o.iPageId;
	iVecId = o.iVecId;
//	bAlloc = o.bAlloc;
//	bDirty = o.bDirty;

	#ifdef DEBUG
	cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "] assigned.\n";
	#endif

	return *this;
}


void BufferFrame::init(int c_file) {
	if (bCopy) {
		cerr << "Can't call init() on a copy.\n";
		throw;
	}
	else { fd = c_file; }
}

void* BufferFrame::getData() {
	if (isAlloc()) { return pPageBuffer; }
	else { return nullptr; } // i.e. return null pointer
}

unsigned BufferFrame::getPageId() {
	return iPageId;
}

int BufferFrame::getVecId() {
	return iVecId;
}

bool BufferFrame::isAlloc() {
	if (bCopy) { return oParent->isAlloc(); }
	else { return bAlloc; }
}

bool BufferFrame::isDirty() {
	if (bCopy) { return oParent->isDirty(); }
	else { return bDirty; }
}

void BufferFrame::setDirty() {
	#ifdef DEBUG
	cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "]" << (bCopy? "(Copy)" : "") << "::setDirty() invoked.\n";
	#endif

	if (bCopy) { oParent->setDirty(); }
	else { bDirty = true; }
}

void BufferFrame::mapPage(unsigned pageId, unsigned size) {
	#ifdef DEBUG
	cout << "BufferFrame[" << iVecId << "]" << (bCopy?"(Copy)":"") << "::mapPage(pageId=" << pageId << ", size="<< size << ") invoked.\n";
	#endif

	if (bCopy) { oParent->mapPage(pageId, size); }
	else {
		iPageId = pageId;
		iPageSize = size;
		bAlloc = true;
		pFileBuffer = mmap(NULL, iPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, iPageId << PAGE_SIZE_OFFSET);
		if (pFileBuffer == MAP_FAILED) {
			cerr << "BufferFrame::mapPage(): Error in mmap(). Return file pointer MAP_FAILED: " << MAP_FAILED << ". Wrong PAGE_SIZE?";
		}
		memcpy(pPageBuffer, pFileBuffer, iPageSize);
	}

	#ifdef DEBUG
	cout << "BufferFrame[" << iVecId << "]" << (bCopy?"(Copy)":"") << "::mapPage(pageId=" << pageId << ", size="<< size << ") return.\n";
	#endif
}

void BufferFrame::freePage() {
	#ifdef DEBUG
	cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "]" << (bCopy?"(Copy)":"") << "::freePage() invoked.\n";
	#endif

	if (bCopy) { oParent->freePage(); }
	else {
		if (bAlloc) {
			if (bDirty) {
				#ifdef DEBUG
				cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "]::freePage(): Page is dirty. Flush to file.\n";
				#endif
				memcpy(pFileBuffer, pPageBuffer, iPageSize);
			}
			#ifdef DEBUG
			cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "]::freePage(): pFileBuffer is being munmap().\n";
			#endif
			if (munmap(pFileBuffer, iPageSize) == -1) {
				cerr << "BufferFrame::freePage(): Error in munmap().\n";;
			}
		}
		#ifdef DEBUG
		else {
			cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "]::freePage(): Page is not allocated. Do nothing.\n";
		}
		#endif
		bAlloc = false;
	}

	#ifdef DEBUG
	cout << "BufferFrame[vecId=" << iVecId << ", pageId=" << iPageId << "]::freePage() return.\n";
	#endif
}

unsigned BufferFrame::size() {
	return iPageSize;
}
